import { Request, Response, NextFunction } from 'express';
import express, { Express } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { TypeSchema, SymbolicTask, qualify, HealthyInfo } from './protocol';

interface TaskStatus {
    status: 'Created' | 'Running' | 'Completed' | 'Failed';
    percentage: number;
    method: string;
    created_at: number;
    completed_at: number | null;
    log: string[];
    log_cursor: number;
    result: any;
    error_message: string | null;
    stack_trace: string | null;
}
interface TaskStatusMap {
    [key: string]: TaskStatus;
}
const taskStatus: TaskStatusMap = {};

interface EdpServiceClass {
  serviceName?: string | null
  desc?: string | null
  category?: string | null
  tasks: string[]
  workflows?: string[]
  app?: Express
}

function trim(str: string) {
    if (typeof str !== 'string') {
        return str;
    }
    if (str.length > 100) {
        return str.slice(0, 100) + '...';
    }
    return str;
}

export function edpService<T extends { new (...args: any[]): any }>(constructor: T & EdpServiceClass) {
    const app: Express = express();
    // 配置请求体大小限制
    // app.use(express.json({limit: '50mb'}));
    app.use(express.text({ type: '*/*', limit: '50mb' }));
    // app.use(express.urlencoded({limit: '50mb', extended: true}));
    
    const cls = constructor;
    const instance = new cls()
    let name = () => {
        if (cls.serviceName) {
            return cls.serviceName
        } else if (instance.serviceName){
            return instance.serviceName
        } else {
            return cls.name
        }
    }
    let desc = () => {
        if (cls.desc) {
            return cls.desc
        } else if (instance.desc) {
            return instance.desc
        } else {
            return "edp service of " + name()
        }
    }
    let category = () => {
        if (cls.category) {
            return cls.category
        } else if (instance.category) {
            return instance.category
        } else {
            return null
        }
    }
    let _tasks = () => {
        if (cls.tasks) {
            return cls.tasks
        } else if (instance.tasks) {
            return instance.tasks
        } else {
            return []
        }
    }
    let tasks = () => {
        return _tasks().map((task: string) => {
            if (task.startsWith('#instant ')) {
                return task.slice(9)
            } else {
                return task
            }
        })
    }
    let task_instant = () => {
        return _tasks().filter((task: string) => task.startsWith('#instant ')).map((task: string) => task.slice(9))
    }
    let task_long = () => {
        return _tasks().filter((task: string) => !task.startsWith('#instant ')).map((task: string) => task)
    }
    let workflows = () => {
        if (cls.workflows) {
            return cls.workflows
        } else if (instance.workflows) {
            return instance.workflows
        } else {
            return []
        }
    }
    function _update(taskId: string, percentage?: number, status?: string, message?: string): boolean {
        if (taskId in taskStatus) {
            if (percentage) {
                taskStatus[taskId].percentage = percentage;
            }
            if (status) {
                taskStatus[taskId].status = status as TaskStatus['status'];
            }
            if (message) {
                taskStatus[taskId].log.push(message);
            }
            return true;
        }
        return false;
    }
    function updateProgress(this: any, percentage: number, status?: string): boolean {
        if (!this.taskId) {
            throw new Error("call `updateProgress()` from unlinked environment");
        }
        return _update(this.taskId, percentage, status);
    }
    function log(this: any, message: string): boolean {
        if (!this.taskId) {
            throw new Error("call `log()` from unlinked environment");
        }
        return _update(this.taskId, undefined, undefined, message);
    }
    function complete(this: any): void {
        if (!this.taskId) {
            throw new Error("call `complete()` from unlinked environment");
        }
        _update(this.taskId, 100, 'Completed');
        taskStatus[this.taskId].completed_at = Date.now();
    }
    function fail(this: any, errmsg: string = 'unknown error'): void {
        if (!this.taskId) {
            throw new Error("call `fail()` from unlinked environment");
        }
        _update(this.taskId, 0, 'Failed');
        taskStatus[this.taskId].completed_at = Date.now();
        taskStatus[this.taskId].error_message = errmsg;
    }
    cls.prototype.updateProgress = updateProgress;
    cls.prototype.log = log;
    cls.prototype.complete = complete;
    cls.prototype.fail = fail;

    app.get('/', (req: Request, res: Response) => {
        res.send(desc());
    });
    app.get('/meta', (req: Request, res: Response) => {
        res.json({
            name: name(),
            desc: desc(),
            category: category()
        });
    });
    app.get('/tasks', (req: Request, res: Response) => {
        res.json(tasks());
    });
    app.get('/workflows', (req: Request, res: Response) => {
        res.json(workflows());
    });
    app.get('/mount', (req, res) => {
        res.json({
            "session_key": uuidv4(),
            "healthy_info": "Healthy"
        })
    })
    let symbolics: [SymbolicTask, boolean][] = _tasks().map((x: string) => {
        if (x.startsWith('#instant ')) {
            return [SymbolicTask.parse(x.slice(9)), true]
        } else {
            return [SymbolicTask.parse(x), false]
        }
    })
    let get_task_method = (cls: T, tname: string) => {
        if (cls.prototype[tname]) {
            const method = cls.prototype[tname];
            return { 
                method, 
                type: 'prototype' as const,
                isInstant: method._is_instant_task === true
            };
        } else if ((cls as any)[tname] && typeof (cls as any)[tname] === 'function') {
            const method = (cls as any)[tname];
            return { 
                method, 
                type: 'static' as const,
                isInstant: method._is_instant_task === true
            };
        } else {
            const instance = new cls();
            if ((instance as any)[tname] && typeof (instance as any)[tname] === 'function') {
                const method = (instance as any)[tname];
                return { 
                    method, 
                    type: 'instance' as const,
                    isInstant: method._is_instant_task === true
                };
            }
        }
        return null;
    }
    for (const [task, isInstant] of symbolics) {
        const taskName = task.name;
        const methodInfo = get_task_method(cls, taskName);
        if (!methodInfo) {
            console.warn(`任务 '${taskName}' 没有找到方法，跳过路由创建`);
            continue;
        }
        app.post(`/task/${taskName}`, async (req: Request, res: Response) => {
            try {
                let data = req.body;
                try {
                    console.log('原始请求体:', typeof data);
                    // while (typeof data === 'string') {
                        data = JSON.parse(data);
                    // }
                    console.log('解析后的数据:', typeof data);
                } catch (e: any) {
                    console.error('解析错误:', e);
                    return res.status(400).json({
                        "status": "Failed",
                        "body": trim(data),
                        "error_message": `无法解析JSON: ${e.message}`
                    });
                }
                if (!qualify(data, task.input_type)) {
                    return res.status(400).json({
                        "status": "Failed",
                        "error_message": `invalid input: ${trim(JSON.stringify(data))} for ${task.input_type.short_str()}`
                    });
                }
                const taskId = uuidv4();
                taskStatus[taskId] = {
                    status: 'Created',
                    percentage: 0,
                    method: taskName,
                    created_at: Date.now(),
                    completed_at: null,
                    log: [],
                    log_cursor: 0,
                    result: null,
                    error_message: null,
                    stack_trace: null
                };
                if (isInstant) {
                    try {
                        let result;
                        if (methodInfo.type === 'prototype') {
                            const instance = new cls();
                            instance.taskId = taskId;
                            result = await methodInfo.method.call(instance, data);
                        } else if (methodInfo.type === 'static') {
                            result = await methodInfo.method(data);
                        } else { // instance
                            const instance = new cls();
                            instance.taskId = taskId;
                            result = await methodInfo.method.call(instance, data);
                        }
                        console.log(`instant task ${taskName} result: ${trim(JSON.stringify(result))}`);
                        res.json({
                            "task_id": "",
                            "progress_pipe_addr": "",
                            "result_addr": "",
                            "log_pipe_addr": "",
                            "instant_result": result,
                            "status": "ResultReceived",
                        });
                    } catch (e: any) {
                        const stackTrace = e.stack;
                        console.error(`任务 '${taskName}' 执行错误:\n${stackTrace}`);
                        _update(taskId, undefined, 'Failed', `错误: ${e.message}`);
                        taskStatus[taskId].error_message = e.message;
                        taskStatus[taskId].stack_trace = stackTrace;
                        taskStatus[taskId].completed_at = Date.now();
                        res.status(500).json({
                            error_message: e.message,
                            stack_trace: stackTrace
                        });
                    }
                } else {
                    setTimeout(async () => {
                        try {
                            _update(taskId, 0, 'Running');
                            let result;
                            if (methodInfo.type === 'prototype') {
                                const instance = new cls();
                                instance.taskId = taskId;
                                result = await methodInfo.method.call(instance, data);
                            } else if (methodInfo.type === 'static') {
                                result = await methodInfo.method(data);
                            } else { // instance
                                const instance = new cls();
                                instance.taskId = taskId;
                                result = await methodInfo.method.call(instance, data);
                            }
                            _update(taskId, 100, 'Completed');
                            taskStatus[taskId].result = result;
                            taskStatus[taskId].completed_at = Date.now();
                            console.log(`task ${taskName} result: ${trim(JSON.stringify(result))}`);
                        } catch (e: any) {
                            const stackTrace = e.stack;
                            console.error(`任务 '${taskName}' 执行错误:\n${stackTrace}`);
                            _update(taskId, undefined, 'Failed', `错误: ${e.message}`);
                            taskStatus[taskId].error_message = e.message;
                            taskStatus[taskId].stack_trace = stackTrace;
                            taskStatus[taskId].completed_at = Date.now();
                        }
                    }, 0);

                    res.json({
                        task_id: taskId,
                        progress_pipe_addr: `/task/${taskName}/progress/${taskId}`,
                        result_pipe_addr: `/task/${taskName}/result/${taskId}`,
                        log_pipe_addr: `/task/${taskName}/log/${taskId}`
                    });
                }
            } catch (e: any) {
                const stackTrace = e.stack;
                console.error(`创建任务时发生错误:\n${stackTrace}`);
                res.status(500).json({
                    error_message: e.message,
                    stack_trace: stackTrace
                });
            }
        });
        if (!methodInfo.isInstant) {
            app.get(`/task/${taskName}/progress/:taskId`, (req: Request, res: Response) => {
                const taskId = req.params.taskId;
                if (!taskStatus[taskId]) {
                    res.status(404).json({
                        error: `任务 ${taskId} 不存在`
                    });
                    return;
                }
                res.json({
                    status: taskStatus[taskId].status,
                    percentage: taskStatus[taskId].percentage
                });
            });
            app.get(`/task/${taskName}/result/:taskId`, (req: Request, res: Response) => {
                const taskId = req.params.taskId;
                if (!taskStatus[taskId]) {
                    res.status(400).json({
                        error: `任务 ${taskId} 不存在`
                    });
                    return;
                }
                const status = taskStatus[taskId].status;
                if (status === 'Completed') {
                    res.json(taskStatus[taskId].result);
                } else if (status === 'Failed') {
                    const response: any = {
                        status,
                        error_message: taskStatus[taskId].error_message || '未知错误'
                    };
                    if (process.env.NODE_ENV === 'development') {
                        response.stack_trace = taskStatus[taskId].stack_trace;
                    }
                    res.status(500).json(response);
                } else {
                    res.status(202).json({ status });
                }
            });
            app.get(`/task/${taskName}/log/:taskId`, (req: Request, res: Response) => {
                const taskId = req.params.taskId;
                if (!taskStatus[taskId]) {
                    res.status(400).json({
                        error: `任务 ${taskId} 不存在`
                    });
                    return;
                }
                const taskLog = taskStatus[taskId].log;
                const taskLogCursor = taskStatus[taskId].log_cursor;
                const newPos = taskLog.length;
                if (newPos > taskLogCursor) {
                    const newLog = taskLog.slice(taskLogCursor, newPos);
                    taskStatus[taskId].log_cursor = newPos;
                    res.json(newLog);
                } else {
                    res.json([]);
                }
            });
        }
    }
    cls.app = app;
    return cls;
} 